/*
2011-06-13
 1. show method 將 value 和 text 資料 escape 左右尖括弧。

2011-06-13
 1. 增加當選單找不到對應的資料時顯示 NO_NAME 可以關閉的機制。

2010-10-08 by jieh
 1. recruit method 臨時改為 SQLMaker 沒有測試！

2008-03-24
 1. 增加功能：可選擇是否啟用將顯示的文字加上選單的 "值"，內定關閉。
*/

package idv.jieh.web.html;

import java.util.*;
import java.sql.*;
//import org.apache.commons.lang.StringEscapeUtils;

/**
 * <p>Exentric Package Version 1.0</p>
 * <p>
 * 這是產生網頁選單的類別。<p>
 * 這個類別將會依照程式設計師自訂的資料產生 HTML 中 selection 的 code 。<br>
 * 說明中的 Value 和 Text 各別代表 HTML selection Tag 內的
 * &lt;option value=<font color=red>Value</font>&gt;<font color=red>Text</font>&lt;/option&gt <p>
 * 當 Values 的內容和 Texts 一樣，可以只設定 Texts ，在產生 code 時會自動產生 Values 部分。
 * <br>
 * 當 Texts 內容是一連續數字時，可以使用 <u>setNumberRange()</u> 產生。
 * <p>
 * <font color=red>
 * 要留意的是，在設定 selection 時，若 Texts 的 size 小於 Values 的 size ，<br>
 * 將會產生 ArrayIndexOutOfBoundsException 的例外。反之，在 Texts 的 size 大於 Values ，<br>
 * 將有一部份 Texts 內容不顯示。因此使用<u>setValues()</u> 及 <u>setTexts()</u> 時要留意其 size 是否一致。<br>
 * </font>
 * </p>
 * @author jieh
 * @version 1.0
 * <br> - class release 2002-05-17 by jieh
 * <br> - doc release 2003-06-21 by Fin
 * <br> - class modify 2002-09-22 by jieh
 */

public class Selection extends idv.jieh.commons.comm.BasicClass implements Cloneable {

  protected boolean textShowKeyValue = false;
  protected boolean textShowNoName = true;

  public void isTextShowKeyValue(boolean b) {
    textShowKeyValue = b;
  }

  public void setTextShowNoName(boolean b){
    textShowNoName = b;
  }

// *************************** 以上為施工區 ***************************

  protected Vector values = new Vector();
  protected Vector texts = new Vector();

  protected String name;
  protected String script = "";
  protected Vector selecteds = new Vector();
  protected boolean disabled = false;

  /**
   * 建構子[1]。<p>
   * 仍未設定 selection Tag 的 Name。將來可以使用 <u>setName() </u> 設定。
   */
  public Selection() {
    this("");
  }

  /**
   * 建構子[2]。<p>
   * 設定這個 selection Tag name。
   * @param name Tag Name
   */
  public Selection(String name) {
    this.name = name;
    values.clear();
    texts.clear();
  }

  /**
   * 複製這個物件。<p>
   * @return 傳回這個物件的副本。
   */

  public Object clone() {
    return clone(new Selection());
  }

  protected Object clone(Selection slt) {
    slt.values = (Vector) values.clone();
    slt.texts = (Vector) texts.clone();
    slt.selecteds = (Vector) selecteds.clone();
    slt.setName(name);
    slt.setScript(script);
    slt.setDisabled(disabled);
    slt.isTextShowKeyValue(textShowKeyValue);
    slt.setTextShowNoName(textShowNoName);

    return slt;
  }

  /**
   * 根據傳入的索引值取得在選單中對應的文字字串。<p>
   * 如果找不到資料則回傳 null。
   * @param idx 索引值。
   * @return String
   */
  public String getText(int idx) {
    return texts.size() > idx ? texts.elementAt(idx).toString() : null;
  }

  /**
   * 根據傳入的索引值取得在選單中對應的 value 字串。<p>
   * 如果找不到資料則回傳 null。
   * @param idx 索引值。
   * @return String
   */
  public String getValue(int idx) {
    return values.size() > idx ? values.elementAt(idx).toString() : null;
  }

  /**
   * 取得選單〝值〞的元素數量。
   * @return int
   */
  public int size() {
    return values.size();
  }

  /**
   * 提供資料庫離線元件 TableCache 物件和兩個欄位以建構選單。
   * @param table 目標 TableCache 物件
   * @param value_column 對應選單 value 存在 TableCache 物件的欄位名稱
   * @param text_column 對應選單 text 存在 TableCache 物件的欄位名稱
   */
  public void recruit(
    idv.jieh.commons.sql.TableCache table,
    String value_column,
    String text_column) {
    setValues(table.getList8ColumnName(value_column));
    setTexts(table.getList8ColumnName(text_column));
  }

  /**
   * 直接指定資料庫的名稱和兩個欄位以建構選單。
   * @param conn 資料庫連線資源
   * @param tablename 目標資料庫檔案名稱(Table Name)
   * @param value_column 對應選單 value 的欄位名稱
   * @param text_column 對應選單 text 的欄位名稱
   * @param condition 查詢該目標資料庫檔案的 SQL where 條件(不含 where 關鍵字)
   */
  public void recruit(
    java.sql.Connection conn,
    String tablename,
    String value_column,
    String text_column,
    String condition) {

    idv.jieh.commons.sql.SQLMaker sm = new idv.jieh.commons.sql.SQLMaker();
    sm.append("select ").append(value_column);
    sm.append(",").append(text_column);
    sm.append(" from ").append(tablename);

    if (condition.length() > 0) {
      sm.append(" where ").append(condition);
    }

    java.sql.ResultSet rs = null;
    try {
      rs = sm.executeQuery(conn);
      while (rs.next()) {
        addContent(rs.getString(1), rs.getString(2));
      }
    } catch (java.sql.SQLException ex) {
      super.dbr.exception(ex);
    } finally {
      try {
        rs.close();
      } catch (SQLException ex) {
        super.dbr.exception(ex);
      }
    }
  }

  /**
   * 取得 Text 內容。<p>
   * 依照指定的 Values 傳回相對應的 Texts 值。
   * @param value 指定一個 Values 值。
   * @return 傳回相對應的 Texts 值。
   */
  public String getText(Object value) {
    int idx = values.indexOf(value);
    if (idx == -1) {
      return value.toString().concat(textShowNoName?" (NO_NAME)":"");
//      return value.toString().concat(" (NO_NAME)");
    } else {
      return texts.elementAt(idx).toString();
    }
  }

  /**
   * 移除一個 Option 選單。<p>
   * 依據指定的 Values ，移除掉相對應的 Text。
   * @param key 指定的 Texts 值。
   */
  public void removeContent8Value(String key) {
    int idx = values.indexOf(key);
    if (values.remove(key)) {
      texts.remove(idx);
    }
  }

  /**
   * 移除一個 Option 選單。<p>
   * 依據指定的 Texts ，移除掉相對應的 Values。
   * @param key 指定的 Values 值。
   */
  public void removeContent8Text(String key) {
    int idx = texts.indexOf(key);
    if (texts.remove(key) && !values.isEmpty()) {
      values.remove(idx);
    }
  }

  /**
   * 在網頁上是否顯示。<p>
   * 設定這個 selection Tag 顯示(enable)或不顯示(disable)<p>
   * 如果 disabled 被開啟，這個選單就不可以被使用者使用，defalut 是關閉的。
   * @param s true：開啟 disabled；false：關閉 disable。
   */
  public void setDisabled(boolean s) {
    this.disabled = s;
  }

  /**
   * 設定這個 Tag 名稱。<p>
   * &lt;select name='<font color='red'><b>NAME</b></font>'&gt;&lt;/select&gt;
   * @param s 名稱。
   */
  public void setName(String s) {
    name = s;
  }

  /**
   * 設定要加入選單內的 JavaScript 語法。<p>
   * &lt;select name='slt' <font color='red'><b>SCRIPT</b></font>&gt;&lt;/select&gt;
   * @param s JavaScript。
   */
  public void setScript(String s) {
    script = s;
  }

  /**
   * 設定預定顯示的項目。<p>
   * @param slteds String[]
   */
  public void setSelected(String ...slteds) {
    selecteds = idv.jieh.commons.util.DataAnalysis.array2vector(slteds);
  }

  /**
   * 設定選單的值。<p>
   * &lt;option values=<font color='red'><b>VECTOR ELEMENT 1</b></font>&gt;item1&lt;/option&gt;
   * <br>
   * &lt;option values=<font color='red'><b>VECTOR ELEMENT 2</b></font>&gt;item2&lt;/option&gt;
   * @param v 傳入一個 Vector Object。
   */
  public void setValues(Vector v) {
    values = v;
  }

  /**
   *  設定選單的字串。<p>
   *  &lt;option &gt;<font color='red'><b>VECTOR ELEMENT 1</b></font>&lt;/option&gt;
   * <br>
   * &lt;option &gt;<font color='red'><b>VECTOR ELEMENT 2</b></font>&lt;/option&gt;
   * @param v 傳入一個 Vector Object。
   */
  public void setTexts(Vector v) {
    texts = v;
  }

  /**
   * 指定數字之間的間距以產生數字選單。<p>
   * 如果你要產生的選單是數字選單，可以直接使用這個方法。<br>
   * 指定起始的數字、結束的數字和之間的間隔值，這個方法會以間隔值產生數字選單。<br>
   * @param s 起始的數字。
   * @param e 結束的數字。
   * @param interval 起始的數字、結束的數字和之間的間隔值。
   */
  public void setNumberRange(int s, int e, int interval) {
    texts.clear();
    values.clear();
    for (int i = s; i <= e; i += interval) {
      texts.addElement(String.valueOf(i));
      // 2003-7-8
      values.addElement(String.valueOf(i));
    }
  }

  /**
   * 產生連續的數字選單。<p>
   * 如果你要產生的選單是數字選單，可以直接使用這個方法。<br>
   * 指定起始的數字和結束的數字，這個方法會產生連續的數字，加入 Texts 中。<br>
   * @param s 起始的數字。
   * @param e 結束的數字。
   */
  public void setNumberRange(int s, int e) {
    this.setNumberRange(s, e, 1);
  }

  /**
   * 加入一組資料到選單。<p>
   * 第一個參數代表選單的值(Value)，第二個參數代表選單的標籤(Text)，這組資料將會出現在
   * 選單的最後。<p>
   * &lt;option value=<font color=red>Value</font>&gt;<font color=red>Text</font>&lt;/option&gt
   * @param value 選單的值。
   * @param text 選單的標籤。
   */
  public void addContent(Object value, Object text) {
    values.addElement(value);
    texts.addElement(text);
  }

  /**
   * <font color=red>(2003-09-22)</font>
   * 依據所指定的索引值設定一組資料到選單。<p>
   * 第一個參數代表選單的值(Value)，第二個參數代表選單的標籤(Text)，
   * 第三個參數是要插入的位置順序。<p>
   * &lt;option value=<font color=red>Value</font>&gt;<font color=red>Text</font>&lt;/option&gt
   * @param value 選單的值。
   * @param text 選單的標籤。
   * @param idx 所指定的索引值。
   */
  public void insertContent(Object value, Object text, int idx) {
    values.insertElementAt(value, idx);
    texts.insertElementAt(text, idx);
  }

  // 在開始拼組 HTML CODE 之前，要先整理一些物件的內容，每個要實作。
  protected void beforeShow() {
    // 2003-07-08 by jieh
    if (values.isEmpty()) {
      values = (Vector) texts.clone();
    }
    // 2005-09-27 by jieh
    if (values.size() != texts.size()) {
      throw new java.lang.IllegalArgumentException("VALUE SET size != TEXT SET size.");
    }
  }

  /**
   * 產生選單的 HTML code。<p>
   * 依照給定的 Texts 或 Values 產生 HTML 下拉式選單 的 code。<br>
   * <font color=red>
   * 如果設定的 文字集(texts) 和 選項值(values) 數量不符則會丟出
   * IllegalArgumentException </font>
   * @return 選單的 HTML code。
   */
  public String show() {
    beforeShow();

    StringBuilder sb = new StringBuilder();
    sb.append("<SELECT NAME='").append(name).append("' ");
    sb.append(script).append(disabled ? " DISABLED>" : ">");

    Iterator itrt = texts.iterator();

    // 2005-09-27 by jieh
    // 避免多餘的 equals 判斷 以增加效能
    for (Object value : values) {
      // 2012-07-16 modify by jieh
      sb.append("<OPTION VALUE='").append(escapeHtml(value.toString())).append("'");
      sb.append(selecteds.contains(value) ? " SELECTED>" : ">");

      // 2008-03-24 add by jieh
      if (textShowKeyValue) {
        sb.append(value);
        if (value.toString().length() > 0) {
          sb.append(":");
        }
      }

      // 引用 http://qingbo.net/blog/post380.html 觀念
      // 只替换左右尖括弧就可以了，雖然還有一些 HTML entity 未被轉換，可能造成輸出的網頁不符合 web 標準，但是大部分流覽器都可以正常顯示也就夠了。
      String text = escapeHtml(itrt.next().toString());
      sb.append(text).append("</OPTION>");
    }
    sb.append("</SELECT>");

    return sb.toString();
  }


  private String escapeHtml(String src){
    return src.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
  }

  /*
    public static void main(String[] args) {
      com.exentric.util.DeBuger dbr = new com.exentric.util.DeBuger("Selection Main Test");
      Selection me = new Selection();
      me.setNumberRange(10, 30, 5);
      me.setSelected("15");
      dbr.normal(me.show());
    }
     //*/

} // class end
